{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 量化配置\n",
    "\n",
    "{class}`~tvm.relay.quantize.QConfig` 通过设置配置变量来配置量化行为。\n",
    "\n",
    "```{note}\n",
    "{class}`~tvm.relay.quantize.QConfig` 对象在 C++ 中由节点系统（node system）支持，并且可以与 Python 和 C++ 之间交换参数。\n",
    "\n",
    "不要直接构造，而是使用 {func}`tvm.relay.quantize.qconfig`。\n",
    "\n",
    "一旦实例被构造，由 C++ 节点支持的字段就是不可变的。有关字段的信息请参见 {data}`tvm.relay.quantize.QConfig._node_defaults`。\n",
    "```\n",
    "\n",
    "````{tip}\n",
    "{func}`tvm.ir.make_node` 通过其类型键和字段创建新的 IR 节点。如果创建的节点是 AttrsNode 的实例，则创建函数还将运行绑定检查和 Attrs 支持的默认值设置。\n",
    "\n",
    "比如：\n",
    "```python\n",
    "x = tvm.ir.make_node(\"IntImm\", dtype=\"int32\", value=10)\n",
    "assert isinstance(x, tvm.tir.IntImm)\n",
    "assert x.value == 10\n",
    "```\n",
    "````"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'nbit_input': 8,\n",
       " 'nbit_weight': 8,\n",
       " 'nbit_activation': 32,\n",
       " 'dtype_input': 'int8',\n",
       " 'dtype_weight': 'int8',\n",
       " 'dtype_activation': 'int32',\n",
       " 'calibrate_mode': 'global_scale',\n",
       " 'global_scale': 8.0,\n",
       " 'weight_scale': 'power2',\n",
       " 'skip_dense_layer': True,\n",
       " 'skip_conv_layers': [0],\n",
       " 'do_simulation': False,\n",
       " 'round_for_shift': True,\n",
       " 'debug_enabled_ops': None,\n",
       " 'rounding': 'UPWARD',\n",
       " 'calibrate_chunk_by': -1,\n",
       " 'partition_conversions': 'disabled'}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import set_env\n",
    "from tvm import relay\n",
    "relay.quantize.QConfig._node_defaults"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "````{note}\n",
    "```c++\n",
    "/*! \\brief Attribute for simulated quantize operator */\n",
    "struct SimulatedQuantizeAttrs : public tvm::AttrsNode<SimulatedQuantizeAttrs> {\n",
    "  int kind;\n",
    "  bool sign;\n",
    "  std::string rounding;\n",
    "\n",
    "  TVM_DECLARE_ATTRS(SimulatedQuantizeAttrs, \"relay.attrs.SimulatedQuantizeAttrs\") {\n",
    "    TVM_ATTR_FIELD(kind).describe(\"kind of field, hint for nbit/dtype configuration.\");\n",
    "    TVM_ATTR_FIELD(sign).set_default(true).describe(\"whether to use signed data type.\");\n",
    "    TVM_ATTR_FIELD(rounding).set_default(\"round\").describe(\n",
    "        \"rounding mode. Can be 'floor', 'ceil', 'round'\");\n",
    "  }\n",
    "};\n",
    "```\n",
    "`SimulatedQuantizeAttrs` 结构体用于表示模拟量化算子的属性。该结构体继承自 `tvm::AttrsNode` 类，并使用 `TVM_DECLARE_ATTRS` 宏来声明属性。\n",
    "\n",
    "在结构体中，定义了三个成员变量：\n",
    "- `kind`：表示字段的类型和配置提示，用于指导量化算子的 nbit/dtype 设置。\n",
    "- `sign`：表示是否使用带符号的数据类型，默认为 `true`。\n",
    "- `rounding`：表示舍入模式，可以是 `floor`（向下取整）、`ceil`（向上取整）或 `round`（四舍五入），默认为 `round`。\n",
    "\n",
    "通过 `TVM_ATTR_FIELD` 宏来指定每个成员变量的名称和描述信息。\n",
    "\n",
    "这个结构体可以用于定义模拟量化算子所需的属性，例如在模型编译期间传递给量化层的配置参数。\n",
    "````"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "用于存储量化配置选项的类：\n",
    "\n",
    "```c++\n",
    "class QConfigNode : public Object {\n",
    " public:\n",
    "  int nbit_input = 8; // 输入张量的位宽，默认为8位。\n",
    "  int nbit_weight = 8; // 权重张量的位宽，默认为8位。\n",
    "  int nbit_activation = 32; // 激活函数输出张量的位宽，默认为32位。\n",
    "  DataType dtype_input = DataType::Int(8); // 输入张量的数据类型，默认为整数类型（8位）。\n",
    "  DataType dtype_weight = DataType::Int(8); // 权重张量的数据类型，默认为整数类型（8位）。\n",
    "  DataType dtype_activation = DataType::Int(32); // 激活函数输出张量的数据类型，默认为整数类型（32位）。\n",
    "  std::string calibrate_mode = \"global_scale\"; // 校准模式\n",
    "  double global_scale = 8.0; // 全局缩放因子\n",
    "  std::string weight_scale = \"power2\"; // 权重缩放模式\n",
    "  bool skip_dense_layer = true; // 是否跳过 dense 层\n",
    "  Array<Expr> skip_conv_layers = Array<Expr>(ObjectPtr<Object>(nullptr)); // 要跳过的卷积层的列表，默认为空。\n",
    "  bool do_simulation = false; // 是否进行模拟计算\n",
    "  bool round_for_shift = true; // 是否为移位操作进行舍入\n",
    "  Array<Expr> debug_enabled_ops = Array<Expr>(ObjectPtr<Object>(nullptr)); // 要启用调试的算子列表，默认为空。\n",
    "  std::string rounding = \"UPWARD\"; // 舍入模式\n",
    "  int calibrate_chunk_by = -1; // 按块进行校准的块大小\n",
    "  std::string partition_conversions = \"disabled\"; // 分区转换模式\n",
    "\n",
    "  // 用于访问和处理属性的成员函数\n",
    "  void VisitAttrs(AttrVisitor* v) {\n",
    "    ...\n",
    "  }\n",
    "\n",
    "  static constexpr const char* _type_key = \"relay.quantize.QConfig\"; // 标识该类的类型\n",
    "  TVM_DECLARE_FINAL_OBJECT_INFO(QConfigNode, Object); // 声明该类为 final 类\n",
    "};\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`QConfig` 类和 `QConfigContext` 结构体，用于管理构建配置的上下文。\n",
    "\n",
    "`QConfig` 类是容器类，它继承自 `ObjectRef` 类。它具有以下成员函数：\n",
    "- 默认构造函数和带有 `ObjectPtr<Object>` 参数的构造函数。\n",
    "- 重载了箭头运算符 `->`，以支持获取 `QConfigNode` 对象。\n",
    "- `EnterQConfigScope` 静态函数，用于将新的 `BuildConfig` 上下文压入线程本地栈中。\n",
    "- `ExitQConfigScope` 静态函数，用于从线程本地上下文栈中弹出 `BuildConfig` 上下文，恢复之前的配置作为当前上下文。\n",
    "- `Current` 静态函数，用于从线程本地存储中获取当前的 `BuildConfig` 上下文，如果没有进入 `BuildConfig` 范围，则返回默认配置。\n",
    "- 使用 `ContainerType` 别名指定 `QConfigNode` 为容器类型。\n",
    "\n",
    "`QConfigContext` 结构体是 RAII（资源获取即初始化）容器，用于提供受控的 `BuildConfig` 上下文。它在构造时将配置压入上下文栈中，并在析构时弹出上下文。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python 端 `tvm.relay.quantize.qconfig` 参数解读：\n",
    "- `nbit_dict: QAnnotateKind`: 每种注释字段的位数。\n",
    "- `calibrate_mode: str`: 校准模式：'global_scale' 或者 'kl_divergence'。\n",
    "    - `global_scale`: 使用 global scale。\n",
    "    - `kl_divergence`: 在数据集中通过 KL 散度查找 scale。\n",
    "- `global_scale: float`: 校准的全局 scale。\n",
    "- `weight_scale: str`: 计算权重的 scale 的方法（用 `QAnnotateKind.WEIGHT` 注解）。\n",
    "    - `power2`: 找到张量绝对值的最大值，然后将其向上取到 $2$ 的幂次方。\n",
    "    - `max`: 找到张量绝对值的最大值。\n",
    "- `skip_dense_layer: bool = True`: 是否跳过所有的 `nn.dense` 层类型。\n",
    "- `skip_conv_layers: list[int]`: 指定要跳过的层。提供索引列表，指示哪些卷积层保持不变。从 $0$ 开始。\n",
    "- `do_simulation: bool`: 是否仅使用浮点运算进行模拟。\n",
    "- `round_for_shift: bool`： 是否在移位时添加舍入偏差。\n",
    "- `debug_enabled_ops: list[str]|None = None`: 部分量化指定的算子进行调试。默认值为 `None`，这意味着将尝试调用所有算子的注解重写函数。\n",
    "- `rounding: str`: 定点乘法的舍入方向。\"UPWARD\" 或者 \"TONEAREST\"。\n",
    "- `partition_conversions: str = 'disabled'`: 可选值：`'disabled'`、`'enabled'` 或者 `'fully_integral'`。如果设置为 `'enabled'` 或 `'fully_integral'`，则将量化的结果划分到模块容器，其中包含前缀函数（由输入转换为量化数据空间组成），中间函数（由核心量化网络组成），后缀函数（由输出反量化组成）以及主函数（依次调用前缀、中间和后缀函数）。如果设置为 `'fully_integral'` 且结果中存在未量化的算子，则会引发异常。默认值为 `'disabled'`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "ai",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
